home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / MATH / PMAT12 / PMAT.EXE / PMAT.DOC < prev    next >
Text File  |  1993-01-24  |  49KB  |  1,311 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.           P-Mat v1.2
  19.           An Turbo Pascal program for Recursive Matrix Algebra
  20.  
  21.           Mark Von Tress, Ph.D.
  22.  
  23.  
  24.  
  25.  
  26.  
  27.  
  28.  
  29.  
  30.  
  31.  
  32.  
  33.  
  34.  
  35.  
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.           PO Box 171173
  46.           Arlington TX 76003
  47.  
  48.  
  49.           Compuserve User ID : 71530,1170
  50.  
  51.  
  52.           Date: January 30, 1993
  53.  
  54.  
  55.  
  56.  
  57.  
  58.  
  59.  
  60.           You are responsible for what you do with the
  61.           code. Here is a formal disclaimer:
  62.  
  63.  
  64.           DISCLAIMER: THIS PROGRAM IS PROVIDED AS IS, WITHOUT ANY
  65.           WARRANTY, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
  66.           TO FITNESS FOR A PARTICULAR PURPOSE. THE AUTHOR DISCLAIMS
  67.           ALL LIABILITY FOR DIRECT OR CONSEQUENTIAL DAMAGES RESULTING
  68.           FROM USE OF THIS PROGRAM.
  69.  
  70.  
  71.           Copyright (c) Mark Von Tress 1993
  72.  
  73.  
  74.  
  75.  
  76.  
  77.           Mat-P 3
  78.  
  79.  
  80.                                   Table of Contents
  81.  
  82.  
  83.           I. INTRODUCTION . . . . . . . . . . . . . . . . . . . . . . .   4
  84.  
  85.           II. FINANCIAL CONDITIONS OF USE . . . . . . . . . . . . . . .   4
  86.  
  87.           III. BASIC ALGORITHMS . . . . . . . . . . . . . . . . . . . .   5
  88.                A. Structures and Allocation . . . . . . . . . . . . . .   5
  89.                B. The global stack: dispatch  . . . . . . . . . . . . .   7
  90.                C. Recursion . . . . . . . . . . . . . . . . . . . . . .   7
  91.                D. Matrix Assignment . . . . . . . . . . . . . . . . . .   8
  92.                E. Parameter Passing . . . . . . . . . . . . . . . . . .   9
  93.  
  94.           IV. FUNCTIONS . . . . . . . . . . . . . . . . . . . . . . . .  10
  95.                A. Error Handling  . . . . . . . . . . . . . . . . . . .  10
  96.                B. Array Allocation or Deallocation  . . . . . . . . . .  11
  97.                C. Stack Control . . . . . . . . . . . . . . . . . . . .  11
  98.                D. Matrix Allocation, Deallocation and Copy  . . . . . .  13
  99.                E. Display Functions . . . . . . . . . . . . . . . . . .  14
  100.                F. Matrix IO . . . . . . . . . . . . . . . . . . . . . .  15
  101.                G. Binary Matrix Functions . . . . . . . . . . . . . . .  16
  102.                H. Unary Matrix Functions  . . . . . . . . . . . . . . .  17
  103.                I. Patterned Matrices  . . . . . . . . . . . . . . . . .  19
  104.                J. Mathematical Functions  . . . . . . . . . . . . . . .  20
  105.  
  106.           V. COMPILATION AND LIMITATIONS  . . . . . . . . . . . . . . .  24
  107.  
  108.           VI. REVISION HISTORY  . . . . . . . . . . . . . . . . . . . .  25
  109.                A.  Version 1.0  . . . . . . . . . . . . . . . . . . . .  25
  110.                B.  Version 1.1  . . . . . . . . . . . . . . . . . . . .  25
  111.                C.  Version 1.2  . . . . . . . . . . . . . . . . . . . .  25
  112.  
  113.  
  114.  
  115.  
  116.  
  117.           Mat-P 4
  118.  
  119.  
  120.           I. INTRODUCTION
  121.  
  122.  
  123.  
  124.           PMAT is Turbo Pascal (TP) source code for recursive matrix
  125.           algebra.  The program is based on another program I wrote in C to
  126.           do the same thing. Both use a stack of matrices to keep track of
  127.           intermediate heap allocations. Once I figured it out in C it was
  128.           pretty easy to step back and do it in Pascal.  The object
  129.           oriented extensions in TP also helped smooth the process. This
  130.           allowed a convenient scheme to allow matrices to be larger than
  131.           64K.
  132.  
  133.           Of course you can't overload operators in TP, so the recursion is
  134.           messy. However, you can keep track of intermediate allocations on
  135.           the heap using PMAT. 
  136.  
  137.                new( a, makematrix( 1, 1 ) );
  138.                x = matequals(x, add( a,add(b,c))); 
  139.  
  140.           This code segment has to keep track of matrix allocations on the
  141.           heap, and then delete the temporary matrices. In this example,
  142.           the sum of B and C is a temporary matrix which would be lost
  143.           without some sort of global memory allocation tracking such as a
  144.           stack.  Its memory should be deleted after the equals function is
  145.           called. The stack helps avoid the assignment of temporaries in
  146.           recursive calls.
  147.  
  148.           On a more personal note, I wrote this program for fun. I started
  149.           on this problem in TP 3.0 when that was the best compiler on the
  150.           market (1984). I never really got a satisfactory answer, so I set
  151.           it down. Then I picked it up in Turbo C, and didn't get a
  152.           satisfactory answer. Then I picked it up in C++, and got a good
  153.           answer in about 1990 (see YAMP on the BPROGB of Compuserve). Then
  154.           I wrote it in C with good results, then TP 6.0 just for old times
  155.           sake. I had forgotten how fast TP compiles. It's a real code
  156.           blaster. Object oriented programming in TP 6 also helped. I guess
  157.           I learned some computer science along the way too. 
  158.  
  159.  
  160.           II. FINANCIAL CONDITIONS OF USE
  161.  
  162.  
  163.           If you like this program, send me $5.00 US (or buy a deserving
  164.           beggar lunch. In either case, find a way to repay my charity.)
  165.           You may distribute the package unchanged. I only plan to
  166.           distribute it electronically. I retain the copyright as proof of
  167.           authorship.
  168.  
  169.  
  170.  
  171.  
  172.  
  173.           Mat-P 5
  174.  
  175.           III. BASIC ALGORITHMS
  176.  
  177.  
  178.                A. Structures and Allocation
  179.  
  180.  
  181.           PMAT has two important objects: vmatrix and mstack. The vmatrix
  182.           object is declared in the interface 
  183.  
  184.                vmatrixptr = ^vmatrix;
  185.                vmatrix = Object
  186.                   r, c : integer;
  187.                   Function m( i, j: integer ): double;
  188.                   Function mm( i, j: integer ) : fp;
  189.                   constructor MakeMatrix( vr, vc: integer );
  190.                   Destructor KillVmatrix;
  191.                   Procedure Garbage;
  192.                   Procedure Show( strng: String );
  193.                   Procedure infomatrix( strng: String );
  194.  
  195.                   private
  196.                      v,vcheck: ^app;
  197.                      nelems : longint;
  198.                      onstack : boolean;
  199.                      Procedure purgevectors;
  200.                      Procedure allocvectors( rr, cc: integer );
  201.                End;
  202.  
  203.  
  204.           The vmatrix contains integers for the dimensions of the matrices,
  205.           an array of pointers to vectors of doubles, the number of
  206.           elements, and a check address. Matrix allocation is based on
  207.           Numerical Recipes in C . An array of addresses of vectors of
  208.           doubles is allocated first. Then, an array is allocated and its
  209.           first address is stored in the address vector. This method allows
  210.           matrices larger than 64K and scatters the rows of the matrix
  211.           throughout the heap. A row is restricted to 8190 doubles to keep
  212.           it under 64K. 
  213.  
  214.           Object oriented programming helped hide the ugly notation
  215.           required for storing and retrieving elements from a vmatrix. You
  216.           use the member function mm(i,j) to store elements in the matrix,
  217.           and m(i,j) to retrieve elements. An example is
  218.  
  219.                x^.mm(i,j)^ := y^.m(i,j);
  220.  
  221.           where x and y are vmatrix pointers. Note the extra ^ in
  222.           x^.mm(i,j)^. This returns the address in the heap for storing an
  223.           element. Using x^.mm(i,j) won't work. Think of element assignment
  224.           as saying "put y^.m(i,j) in the address pointed to by
  225.           x^.mm(i,j)". Using m(i,j) returns a double via
  226.  
  227.  
  228.  
  229.  
  230.  
  231.           Mat-P 6
  232.  
  233.                m := v^[i]^[j];
  234.  
  235.  
  236.           and using mm(i,j) returns an address via
  237.  
  238.               mm := @v^[i]^[j];
  239.  
  240.           Using member functions protects you from having to type these
  241.           unintuitive indexing operations. I also allows easy access to
  242.           matrices larger than 64K. They also do range checking on the
  243.           indexes.
  244.  
  245.           The check pointer contains the value of v upon allocating the
  246.           matrix. Then it is set to zero upon deallocation of v. The check
  247.           value is used in the member function Garbage(). If v does not
  248.           equal vcheck, then the matrix has been deallocated, or memory may
  249.           have been corrupted. The program halts if you try to use a
  250.           garbage matrix (better safe than sorry!). 
  251.  
  252.           The boolean variable, onstack, indicates that the matrix is on
  253.           the stack (onstack = true) or not (onstack = false). This
  254.           controls how matrices are copied. Its initial value is false. It
  255.           is set to true by push(), and false by pop(). See CopySD().
  256.  
  257.           PMAT expects you to use pointers to matrices, so allocation looks
  258.           like this
  259.  
  260.           procedure junk;
  261.           var x,y,z : vmatrixptr;
  262.           begin
  263.                new( x, makematrix( 1, 1 ) );
  264.                new( y, makematrix( 1, 1 ) );
  265.                new( z, makematrix( 1, 1 ) );
  266.                         .
  267.                         .
  268.                         .
  269.                { put code that uses x,y,z }
  270.                         .
  271.                         .
  272.                         .
  273.               dispose( x, killvmatrix );
  274.               dispose( y, killvmatrix );
  275.               dispose( z, killvmatrix );
  276.           end;
  277.                   
  278.           Call the constructor for each matrix, then make sure to dispose
  279.           of the storage at the end of the procedure. Your program will
  280.           develop memory leaks if you do not dispose of the vmatrix
  281.           pointers. PMAT is not consistent with the MARK-RELEASE method of
  282.           allocating memory.
  283.  
  284.  
  285.  
  286.  
  287.  
  288.           Mat-P 7
  289.  
  290.           The mstack type is 
  291.  
  292.                mstackptr = ^mstack;
  293.                mstack = Object
  294.                   constructor InitMatrixStack;
  295.                   Destructor KillMatrixStack;
  296.                   Procedure inclevel;
  297.                   Procedure declevel;
  298.                   Function ReturnMat: vmatrixptr;
  299.                   Function DecReturn: vmatrixptr;
  300.                   Procedure push( Var a: vmatrixptr );
  301.                   Procedure nrerror( strng: String );
  302.                   Procedure DumpStack;
  303.  
  304.                   private
  305.                     nummats, level : integer;
  306.                     next : mstackptr;
  307.                     mv : vmatrixptr;
  308.                     Procedure cleanstack( a: vmatrixptr );
  309.                     Function pop: vmatrixptr;
  310.                End;
  311.  
  312.  
  313.  
  314.           The mstack structure is a typical single linked list sort of
  315.           thing. The nummats field contains the number of matrices on the
  316.           stack. The level field contains the subroutine nesting level
  317.           which keeps track of which temporary matrices to be disposed. As
  318.           you enter and leave functions that use matrix assignment, you
  319.           increment and decrement the function nesting level. The
  320.           mstackptr, next, is a pointer to the next mstack object. The
  321.           vmatrixptr mv is the address of a vmatrix object referenced by
  322.           the stack object. 
  323.  
  324.  
  325.                B. The global stack: dispatch
  326.  
  327.           Mstack objects are stored in the global stack linked list with a
  328.           base pointer called dispatch. Dispatch is automatically
  329.           initialized in the use statement. 
  330.  
  331.           Dereference items in dispatch using the ^. operator, such as
  332.           dispatch^.level. The stack also has a tail, so if you try to
  333.           deallocate it, the program stops. It has a value of nil in the
  334.           field called next.
  335.  
  336.                C. Recursion
  337.  
  338.           The recursive matrix functions push mstack pointers onto the
  339.           stack. The address of the matrix reference is stored in mv. You
  340.           create stack elements when you call the push function. mstack
  341.           elements are deallocated automatically by other functions, so you
  342.  
  343.  
  344.  
  345.  
  346.  
  347.           Mat-P 8
  348.  
  349.           don't have to do it.  Popping occurs during calls to matequals()
  350.           for matrix assignment. Temporary mstack pointers are freed during
  351.           matequals by the garbage collector function, CleanStack(). You do
  352.           not have to pop explicitly. 
  353.  
  354.           The only part of mstack that you have to pay attention to is the
  355.           function nesting level. Garbage collection occurs during
  356.           assignment. Any function that uses matrix assignment pushes
  357.           matrices onto dispatch. Stack elements are deleted during garbage
  358.           collection if their value of level is greater than or equal to
  359.           the value of level held in dispatch. This way, each function has
  360.           its own stack, but all stack elements are stored in the same
  361.           linked list.
  362.  
  363.           You control level with the member functions, Inclevel(),
  364.           Declevel(), and DecReturn(). Inclevel() and Declevel() increment
  365.           or decrement level. Declevel() checks that level has not become
  366.           negative. The program stops if it has. 
  367.  
  368.           DecReturn() is used for returning matrices from functions. It
  369.           decrements the nesting level, and returns the address of the
  370.           matrix, v, in dispatch^.next. An example of returning a matrix
  371.           pointer recursively is 
  372.  
  373.  
  374.           function f1: vmatrixptr;
  375.           var x: vmatrixptr;
  376.           begin
  377.              dispatch^.inclevel;
  378.              new( x, makematrix(1,1));
  379.              x := matequals(x, ident(5) );
  380.              dispatch^.push(x);
  381.              f1 := dispatch^.decreturn;
  382.           end;
  383.  
  384.           The stack works by manipulating pointers to vmatrix objects,
  385.           rather than copies. This is faster and uses less memory. The
  386.           matrix pointer is pushed onto the stack. Then the function level
  387.           is decremented, and the pointer x is returned as
  388.           dispatch^.next^.mv. There is no need to dispose x, since it is
  389.           just a pointer stored in a global stack. The stack functions will
  390.           dispose it when it is appropriate.
  391.  
  392.                D. Matrix Assignment
  393.  
  394.           Matrix assignment is accomplished using the function matequals,
  395.           having prototype
  396.  
  397.           Function matequals(Var lop: vmatrixptr; rop: vmatrixptr) :        
  398.                  vmatrixptr;
  399.  
  400.           The statement from the example above uses assignment
  401.  
  402.  
  403.  
  404.  
  405.  
  406.           Mat-P 9
  407.  
  408.              x := matequals( x, ident(5) );
  409.  
  410.           The matrix being assigned must be used in two places. The first
  411.           place is in the function call where the contents of the matrix
  412.           objects are manipulated. The second is on the left of the equals
  413.           sign where the current function is told where the new address of
  414.           x is to be found.
  415.  
  416.           Matequals first checks that the two inputs are not garbage. Then
  417.           it checks if the two inputs point to the same 2 dimensional
  418.           array. If they do, then the matrix a gets a new 2 dimensional
  419.           array. The contents of rop are copied into lop. Then the
  420.           intermediate stack matrices are deleted. 
  421.  
  422.  
  423.                E. Parameter Passing
  424.  
  425.  
  426.           Parameter passing of matrices is the same as in ordinary Pascal.
  427.           If you want to change the contents of a matrix that was passed as
  428.           a parameter, then declare it a var parameter in the procedure.
  429.           Make sure that the matrix is correctly initialized before the
  430.           call. Then use matrix assignment to make the changes in the
  431.           matrix. Here is an example.
  432.  
  433.           procedure f1( var a: vmatrixptr );
  434.           begin
  435.              dispatch^.inclevel;
  436.              a := matequals(a,ident(3));
  437.              dispatch^.declevel;
  438.           end;
  439.           procedure f2( void );
  440.           var x:vmatrixptr;
  441.           begin
  442.                dispatch^.inclevel;
  443.                new(x,makematrix(1,1));
  444.                x^.show('before pass');
  445.                f1(x);
  446.                x^.show('after pass');
  447.                dispose(x,killvmatrix);
  448.                dispatch^.declevel;
  449.           end;
  450.  
  451.  
  452.           The call to  f1 in f2() passes x as a var vmatrix pointer. The
  453.           function call changes the contents of x from a 1x1 matrix to the
  454.           3x3 identity matrix.
  455.  
  456.           This example clarifies why matequals() needs to use the formal
  457.           parameter name on the left of the equals sign. You have to tell
  458.           the program where you put the new copy of the vmatrix.
  459.  
  460.  
  461.  
  462.  
  463.  
  464.           Mat-P 10
  465.  
  466.  
  467.  
  468.  
  469.           IV. FUNCTIONS
  470.  
  471.  
  472.           There are several kinds of functions in PMAT. They fall in
  473.           several categories:
  474.  
  475.                0. error handling
  476.                1. array allocation or deallocation
  477.                2. stack control
  478.                3. matrix allocation, deallocation and copy
  479.                4. display
  480.                5. IO
  481.                6. binary matrix functions
  482.                7. unary matrix function
  483.                8. patterned matrices
  484.                9. mathematical functions
  485.  
  486.           The interface should provide a quick reference to the names and
  487.           spellings. The following discussion explains the header.
  488.  
  489.                A. Error Handling
  490.  
  491.  
  492.           Procedure nrerror( strng: String );
  493.  
  494.           This is the error handler from Numerical Recipes in C. It is
  495.           called whenever a fatal error occurs. It prints the error text
  496.           and exits with a value 1. It is a member function of mstack
  497.           objects. Call it as dispatch^.nrerror('something''s happening
  498.           here');
  499.  
  500.  
  501.           Procedure Garbage;
  502.  
  503.           This function checks if the matrix a has been deleted or
  504.           corrupted. It calls nrerror if it has. This is a vmatrix member
  505.           function. Call it as x^.Garbage;
  506.  
  507.  
  508.  
  509.  
  510.  
  511.           Mat-P 11
  512.  
  513.                B. Array Allocation or Deallocation
  514.  
  515.           The declarations form the structure for matrices.
  516.  
  517.                ap = Array[1..1] Of double;
  518.                apptr = ^ap;
  519.                app = Array[1..1] Of apptr;
  520.  
  521.           The type ap is an array of doubles. Note that the range is from 1
  522.           to 1, and the array is subscriptable and variable length. Range
  523.           checking is turned off in the matrix allocation and indexing
  524.           functions so that these arrays act like they do in C. Each row of
  525.           the matrix is allocated separately by new(apptr). Each new apptr
  526.           is stored in an subscripatble, variable length array of pointers
  527.           to arrays of doubles.
  528.  
  529.  
  530.           Procedure allocvectors( rr, cc: integer )
  531.  
  532.           Arrays are allocated using the Numerical Recipes in C method for
  533.           dmatrix arrays. Matrices in PMat are indexed from 1 to r, and 1
  534.           to c. 
  535.  
  536.  
  537.           procedure purgevectors.
  538.  
  539.           Arrays are freed using the Numerical Recipes in C method for
  540.           dmatrix arrays. 
  541.  
  542.           Neither of these functions need to be called directly since they
  543.           are called by the vmatrix construction routines. They are private
  544.           to the vmatrix class, so they cannot be used outside of the
  545.           pmat.pas unit.
  546.  
  547.  
  548.                C. Stack Control
  549.  
  550.           dispatch: mstackptr; (* stack top: global *)
  551.  
  552.           This declaration in the interface makes dispatch available to all
  553.           units that use pmat. It is allocated automatically from the use
  554.           statement.
  555.  
  556.  
  557.           constructor InitMatrixStack; (* set up stack top and tail *)
  558.  
  559.           This function allocates dispatch and the stack tail. It is called
  560.           by the pmat initialization routine.
  561.  
  562.           Procedure push( Var a: vmatrixptr ); { push a matrix onto stack }
  563.  
  564.           pushes a matrix onto the stack. It should be called before a f1
  565.  
  566.  
  567.  
  568.  
  569.  
  570.           Mat-P 12
  571.  
  572.           := dispatch^.ReturnMat; or f1 := dispatch^.DecReturn; call. The
  573.           new matrix is stored in dispatch^.next^.mv. push() also sets
  574.           a^.onstack to true. This is a member function of mstack.
  575.  
  576.  
  577.           Function pop: vmatrixptr; (* free dispatch->next, return v*)
  578.  
  579.           pop is a private function of the matrix stack. You should not
  580.           call it. It is called repetitively by CleanStack to delete
  581.           temporary matrices. It deletes the matrix structure, and returns
  582.           the matrix pointer mv. pop() also sets mv^.onstack to false.
  583.  
  584.  
  585.           vmatrix *ReturnMat( void );    /* return stack top matrix */
  586.  
  587.  
  588.           ReturnMat() returns the value dispatch^.next^.v in functions that
  589.           have not incremented the function nesting level, i.e. required
  590.           matrix assignment. It does not modify the subroutine nesting
  591.           level. All of the binary matrix functions push a vmatrix pointer
  592.           onto the stack, and then return by the statement "f1 :=
  593.           dispatch^.ReturnMat;". It is a public member function of mstack.
  594.  
  595.  
  596.           Function DecReturn: vmatrixptr; (* return stack to 
  597.                                             matrix,decrement level*)
  598.  
  599.           DecReturn returns the value dispatch^.next^.v in functions that
  600.           have incremented the function nesting level, i.e. required matrix
  601.           assignment. It decrements the subroutine nesting level. Functions
  602.           that use matrix assignment, such as inv(), push matrix pointers
  603.           onto the stack, and then return by the statement 
  604.           "f1 := dispatch^.DecReturn;". This is a public member function of
  605.           mstack.
  606.  
  607.  
  608.           Procedure inclevel; (* increment function nesting level *)
  609.  
  610.           Inclevel() increments the function nesting level. See the
  611.           discussion of the stack and recursion. This a public member
  612.           function of mstack, and is called as dispatch^.inclevel.
  613.  
  614.  
  615.           Procedure declevel; (* decrement function nesting level *)
  616.  
  617.           Declevel() decrements the function nesting level. It also checks
  618.           that the level has become negative. If so, it calls nrerror().
  619.           This indicates that level has been decremented more often than it
  620.           has been incremented. Check the Inclevel()-Declevel() pairs, or
  621.           the Inclevel()-DecReturn() pairs. See the discussion of the stack
  622.           and recursion. This a public member function of mstack, and is
  623.           called as dispatch^.declevel. Use this function when you use
  624.  
  625.  
  626.  
  627.  
  628.  
  629.           Mat-P 13
  630.  
  631.           matrix assignment in a procedure, but do not want to return a
  632.           matrix.
  633.  
  634.  
  635.           Procedure cleanstack( a: vmatrixptr );
  636.                   (* pop stack elements if level >= dispatch^.level *)
  637.  
  638.           CleanStack() is a private function of the stack. It should not be
  639.           called by the user. The argument is a matrix that is not to be
  640.           freed. Thus the call "CleanStack( x )" indicates that all
  641.           matrices with level at or above  dispatch^.level should be freed,
  642.           except for matrices that have the same address as x. It calls pop
  643.           while (m^.level >= dispatch^.level), and (dispatch^.next^.next !=
  644.           NIL). It frees all matrices not equal to x.
  645.  
  646.  
  647.           Function matequals( Var lop: vmatrixptr; rop: vmatrixptr ) :
  648.           vmatrixptr; (* copy rop into lop *)
  649.  
  650.           The matrix being assigned must be used in two places. The first
  651.           place is in the function call where the contents of the matrix
  652.           structure are manipulated. The second is on the left of the
  653.           equals sign where the current function is told where the new
  654.           address of x is to be found.
  655.  
  656.           matequals() first checks that the two inputs are not garbage. The
  657.           contents of rop are copied into lop by CopySD(). Then the
  658.           intermediate stack matrices are deleted by CleanStack().
  659.  
  660.  
  661.                D. Matrix Allocation, Deallocation and Copy
  662.  
  663.           constructor MakeMatrix( vr, vc: integer );
  664.                (* allocate a matrix *)
  665.  
  666.           MakeMatrix() constructs a matrix with vr rows and vc columns.
  667.           This should be called to properly construct a matrix before using
  668.           it in the matrix functions. This is a public member function of
  669.           vmatrix, and should be called as new(x, makematrix(1,1));
  670.  
  671.  
  672.           Destructor KillVmatrix; (* deallocate a matrix *)
  673.  
  674.           This frees the matrix storage in m^.v, and then dispose m. It
  675.           calls nrerror if m is corrupted. It is a public member function
  676.           of vmatrix and should be called as dispose(m,killvmatrix);
  677.  
  678.  
  679.           Procedure CopySD( Var source, dest: vmatrixptr );  
  680.                                     (* copy a matrix from source to dest *)
  681.  
  682.           This function should be considered private to the stack function,
  683.  
  684.  
  685.  
  686.  
  687.  
  688.           Mat-P 14
  689.  
  690.           matequals(). It is also hidden to the user in the implementation
  691.           of pmat. 
  692.  
  693.           CopySD() replaces the contents in the destination matrix by a
  694.           copy of the contents in the source matrix. If source and dest are
  695.           the same, then CopySD() returns immediately since you are trying
  696.           to copy a matrix onto itself. 
  697.  
  698.  
  699.           CopySD() recognizes when matrices are being copied from the
  700.           stack. Matrices being copied from the stack are about to be
  701.           deleted by Cleanstack(), so their matrix pointer is popped into
  702.           the destination matrix pointer. The destination matrix storage is
  703.           released first. The indices are reset. Next, the stack matrix
  704.           pointer is popped into the destination matrix pointer. Then the
  705.           function returns. 
  706.  
  707.           If the matrices point to the same dmatrix, and the source matrix
  708.           is not on the stack, then a new dmatrix array is allocated for
  709.           dest. If they have a different number of rows or columns, the
  710.           destination 2D array is reallocated to have the same number of
  711.           rows and columns. Then the dmatrix array contents of the source
  712.           are copied into the destination.
  713.  
  714.  
  715.  
  716.                E. Display Functions
  717.  
  718.  
  719.           Procedure DumpStack;
  720.  
  721.           This dumps the stack. It is the only function that may be called
  722.           between push and the function return. It is for debugging
  723.           purposes. It is a public member function of mstack and should be
  724.           called as dispatch^.dumpstack;
  725.  
  726.  
  727.           Procedure infomatrix( strng: String ); (* show dimensions *)
  728.  
  729.  
  730.           infomatrix() displays information about the matrix. The strng is
  731.           a string describing the matrix. The string, strng, is displayed,
  732.           then the number of rows, columns, m, and mcheck.  Use
  733.           infomatrix() when the matrix is large, or when you are having
  734.           problems with corrupted or deleted matrices. This is a public
  735.           function of vmatrix and should be called as x^.Infomat('something
  736.           is happening here');
  737.  
  738.  
  739.  
  740.  
  741.  
  742.           Mat-P 15
  743.  
  744.           Procedure Show( strng: String );(* display matrix *)
  745.  
  746.           show() displays a matrix. The string, strng is also displayed
  747.           before the matrix elements. You can control the width and decimal
  748.           places by calling setwid() and setdec() before calling show().
  749.           This is public function of vmatrix and a  typical call is
  750.           "x^.show("x");"
  751.  
  752.  
  753.  
  754.           Procedure setwid( wid: integer );      (* set display width *)
  755.           Procedure setdec( decimals: integer ); (* set display decimals *)
  756.           Function getwid: integer;              (* get display width *)
  757.           Function getdec: integer;              (* get display decimals *)
  758.  
  759.  
  760.           setwid() and setdec() store hidden global integers for the
  761.           display width and decimals. show() and Writea() call getwid() and
  762.           getdec() to use these variables.  
  763.  
  764.  
  765.  
  766.                F. Matrix IO
  767.  
  768.  
  769.           External matrices are stored in an ASCII character format. The
  770.           first row is a title string that is at most 80 characters
  771.           including the final carriage return, '\n'. The second row
  772.           contains two integers separated by white space. The first integer
  773.           is the number of rows, r, and the second is the number of
  774.           columns, c. The next rows are the rows of the matrix. Matrix may
  775.           span several text rows, but there must be r*c elements. 
  776.  
  777.           Function reada( fid: String ): vmatrixptr; 
  778.                                  (* read ascii matrix *)
  779.  
  780.  
  781.           Reada() reads a matrix stored in the ASCII file pointed to by
  782.           fid. Reada() terminates if the title string is longer than 80
  783.           total characters, including the terminal carriage return, '\n'.
  784.           Reada() should be called in a matrix assignment since it returns
  785.           a matrix off of the stack. As an example,
  786.  
  787.                x := matequals( x, Reada('xmatrix.dat');
  788.  
  789.  
  790.           Procedure writea( fid: String; a: vmatrixptr; titlestr: String );
  791.                                 (* write ascii matrix *)
  792.  
  793.           Writea() stores a vmatrix, a, in file, fid, using the PMat ASCII
  794.           file format. Data is written to fid using the current print
  795.           display values found in setdec() and setwid(). The title string
  796.  
  797.  
  798.  
  799.  
  800.  
  801.           Mat-P 16
  802.  
  803.           is truncated to 80 characters (including '\n') if it is longer
  804.           than 80 characters.
  805.  
  806.  
  807.  
  808.                G. Binary Matrix Functions
  809.  
  810.  
  811.           The binary matrix functions are recursive in that they push
  812.           matrices onto the stack, and do not manipulate the function
  813.           nesting level. They all push a vmatrix pointer onto the stack,
  814.           and return a vmatrix pointer using a call to ReturnMat(). The
  815.           incoming matrices are checked by calling Garbage() before doing
  816.           any calculations. A typical use of these functions is 
  817.  
  818.  
  819.                (*  d= a+(b-c) ; *)
  820.                d = equals( d, add(a,sub(b,c)));
  821.  
  822.           The call to equals deletes the temporary difference of c from b.
  823.           Scalars may also be used in binary operations. Each of the binary
  824.           matrix functions have "sc" appended to the left(right) if a
  825.           scalar is to be added to a matrix on the left(right).
  826.  
  827.  
  828.           function add( a, b: vmatrixptr ): vmatrixptr;  (* addition a+b *)
  829.           function scadd( a: double; b: vmatrixptr): vmatrixptr;
  830.           function addsc( b: vmatrixptr; a: double); vmatrixptr;
  831.  
  832.           Matrix addition, conditions : a^.r == b^.r and a^.c == b^.c
  833.           The scalar additions add a double to a matrix.
  834.  
  835.  
  836.           Function sub( A, B: vmatrixptr ):vmatrixptr; (* subtraction a-b*)
  837.           Function scsub( a: double; b: vmatrixptr): vmatrixptr;
  838.           Function subsc( b: vmatrixptr; a: double); vmatrixptr;
  839.  
  840.           Matrix subtraction, conditions : a^.r = b^.r and a^.c = b^.c
  841.           The scalar subtractions perform the subraction in the indicated
  842.           order: scsub(i,j) = A - b(i,j), subsc(i,j)= b(i,j)-A.
  843.  
  844.  
  845.           Function Mult( A, B: vmatrixptr ):vmatrixptr;(*matrix mult a*b*)
  846.  
  847.           Caley multiplication, inner products are formed using extended
  848.           doubles, then truncated back to doubles when stored in a matrix.  
  849.                conditions : a^.c = b^.r.
  850.  
  851.  
  852.  
  853.  
  854.  
  855.           Mat-P 17
  856.  
  857.           Function emult( a, b: vmatrixptr ):vmatrixptr;
  858.                                     (* elementwise mult a#b *)
  859.           function scmult( a: double; b: vmatrixptr): vmatrixptr;
  860.           function multsc( b: vmatrixptr; a: double); vmatrixptr;
  861.  
  862.  
  863.           Elementwise multiplication, 
  864.               conditions : a^.r = b^.r and a^.c = b^.c
  865.           The scalar multiplications multiply the elements of b by a.
  866.  
  867.  
  868.  
  869.           Function divis(a,b:vmatrixptr): vmatrixptr;
  870.           function scdivis( a: double; b: vmatrixptr): vmatrixptr;
  871.           function divissc( b: vmatrixptr; a: double); vmatrixptr;
  872.  
  873.  
  874.           Elementwise division,
  875.                 conditions : a^.r = b^.r and a^.c = b^.c and for all i,j    
  876.              b^.m(i,j) <> 0
  877.           The scalar divisions perform division in the indicated order:
  878.           scdivis(i,j) = A/b(i,j), divissc(i,j)= b(i,j)/A.
  879.  
  880.  
  881.  
  882.                H. Unary Matrix Functions
  883.  
  884.  
  885.           The unary matrix functions, neg(), tran(), and inv(), are
  886.           recursive in that they push matrices onto the stack, and do not
  887.           manipulate the function nesting level. neg(), and tran() push a
  888.           vmatrix pointer onto the stack, and return a vmatrix pointer
  889.           using a call to ReturnMat(). inv() uses matrix assignment so  it
  890.           increments the nesting level, pushes the return matrix, and
  891.           returns by a call to DecReturn(). The incoming matrices are
  892.           checked by calling Garbage() before doing any calculations. A
  893.           typical use of these functions is 
  894.  
  895.  
  896.  
  897.  
  898.  
  899.           Mat-P 18
  900.  
  901.           function regression: vmatrixptr;
  902.           var x,y,xpx,xpy,beta : vmatrixptr;
  903.           begin
  904.                (* beta = inv(x'x)x'y *)
  905.                dispatch^.Inclevel;
  906.                new(x, makematrix(1,1));
  907.                new(y, makematrix(1,1));
  908.                new(xpx, makematrix(1,1));
  909.                new(xpy, makematrix(1,1));
  910.                new(beta, makematrix(1,1));
  911.                x := matequals( x, Reada( "x.dat") );
  912.                y := matequals( y, Reada( "y.dat") );
  913.                xpx := matequals( xpx, mult( tran(x), x ));
  914.                xpy := matequals( xpy, mult( tran(x), y ));
  915.                beta := matequals( beta, mult( inv(xpx), xpy));
  916.                dispose( x, killvmatrix);
  917.                dispose( y, killvmatrix);
  918.                dispose( xpx, killvmatrix);
  919.                dispose( xpy, killvmatrix);
  920.                dispatch^.push( beta );
  921.                regression := dispatch^.DecReturn;
  922.           end;
  923.  
  924.           function sweepreg: vmatrixptr;
  925.           var xy, xypxy, beta : vmatrixptr;
  926.           begin
  927.                (* beta using sweep *)
  928.                dispatch^.Inclevel;
  929.                new(xy, makematrix(1,1));
  930.                new(xypxy, makematrix(1,1));
  931.                new(beta, makematrix(1,1));
  932.                xy := matequals( xy, Reada( "xy.dat") );
  933.                xypxy := matequals( xypxy, mult( tran(xy), xy ));
  934.                xypxy := equals( xypxy, sweep( xypxy, 1, xy^.c - 1) );
  935.                beta := matequals( beta, 
  936.                          submat( xypxy, 1, xy^.c -1, xy^.c, xy^.c) );
  937.                dispose( xy, killvmatrix);
  938.                dispose( xypxy, killvmatrix);
  939.                dispatch^.push( beta );
  940.                sweepreg := dispatch^.DecReturn;
  941.           end;
  942.  
  943.  
  944.           Function neg( A: vmatrixptr ): vmatrixptr;(* negation *)
  945.  
  946.           Changes the sign of all elements.
  947.  
  948.  
  949.           Function tran( a: vmatrixptr ): vmatrixptr;(* transpose *)
  950.  
  951.           Matrix transpose: c^.m(i,j) = a^.m(j,i)
  952.  
  953.  
  954.  
  955.  
  956.  
  957.           Mat-P 19
  958.  
  959.           Function sweep( A: vmatrixptr; k1, k2: integer ) : vmatrixptr; 
  960.  
  961.           Sweep cols k1...k2 along the diagonal and in place in a. See the
  962.           example for how to use sweep for regression. Input matrix must be
  963.           square. Row and columns having pivot elements less than 10E-8 are
  964.           set to zero. This indicates a colinearity with at least one
  965.           column that has already been swept out.
  966.  
  967.  
  968.           Function inv( Amat: vmatrixptr ): vmatrixptr;
  969.                                  (* inversion by sweep *)
  970.  
  971.           Calls sweep for inversion: x = matequals( x, sweep(x, 1, x^.c ));
  972.           This is a Gaussian elimination inverter without column pivoting.
  973.           It fails to accurately invert Hilbert matrices of dimension 8 or
  974.           more, which is reasonable for inversion by Gaussian elimination.
  975.  
  976.  
  977.  
  978.  
  979.                I. Patterned Matrices
  980.  
  981.           The patterned matrix functions are recursive. They push matrices
  982.           onto the stack and return by calling ReturnMat. They should be
  983.           used in conjunction with matrix assignment.
  984.  
  985.  
  986.  
  987.           Function submat( a: vmatrixptr; lr, r, lc, c: integer ):          
  988.                     vmatrixptr;    (*submatrix*)
  989.  
  990.           Extract a submatrix of a, using rows lr to r, and columns lc to
  991.           c. All integer arguments must be in the range of the dimension of
  992.           a.
  993.  
  994.           Function ident( n: integer ): vmatrixptr;   (*identity matrix*)
  995.  
  996.           Construct an n dimensional Identity matrix
  997.  
  998.           Function fill( rr, cc: integer; d: double ):vmatrixptr;
  999.                                    (* matrix of constants d *)
  1000.  
  1001.           Construct a rr x cc matrix of constants, d. 
  1002.  
  1003.           Function cv( a, b: vmatrixptr ): vmatrixptr;
  1004.                                   (* vertical concatenation *)
  1005.  
  1006.           Vertically concatenate b to a. a is on top, b is on bottom, and
  1007.           they must have the same number of columns.
  1008.  
  1009.  
  1010.  
  1011.  
  1012.  
  1013.           Mat-P 20
  1014.  
  1015.           Function ch( a, b: vmatrixptr ): vmatrixptr;
  1016.                   (* horizontal concatenation *)
  1017.  
  1018.           Horizontally concatenate b to a. a is on the left, b is on the
  1019.           right, and they must have the same number of rows.
  1020.  
  1021.  
  1022.           vmatrix *vecdiag( vmatrix *a );  
  1023.                                        (* place vector on diagonal *)
  1024.  
  1025.           Place elements of a rx1 or 1xc matrix on the diagonal of a square
  1026.           matrix.  
  1027.  
  1028.  
  1029.                J. Mathematical Functions
  1030.  
  1031.  
  1032.           The mathematical function module for YAMP was translated for
  1033.           PMAT. These are some of the many linear algebra functions that
  1034.           are used.
  1035.  
  1036.  
  1037.           function MSort(a :vmatrixptr; col, order: integer): vmatrixptr;
  1038.  
  1039.           Sort the rows of a matrix by a column if order is not equal to
  1040.           zero. If order is zero, then sort the columns of a matrix by a
  1041.           row.
  1042.  
  1043.  
  1044.           function Mexp(ROp: vmatrixptr): vmatrixptr;
  1045.           function Mabs(ROp: vmatrixptr): vmatrixptr;
  1046.           function Mlog(ROp :vmatrixptr): vmatrixptr;
  1047.           function Msqrt(ROp: vmatrixptr): vmatrixptr;
  1048.  
  1049.           These functions operate on the elements of a matrix. They halt if
  1050.           an argument is out of range. The take exp(a^.m(i,j)),
  1051.           abs(a^.m(i,j)), ln( a^.m(i,j)), and sqrt(a^.m(i,j)). You might
  1052.           consider adding the trig functions too.
  1053.  
  1054.  
  1055.           function Trace(ROp: vmatrixptr): double;
  1056.  
  1057.           Takes the sum of the diagonal elements of a square matrix.
  1058.  
  1059.  
  1060.           function Helm (n: integer): vmatrixptr;
  1061.  
  1062.           Returns the Helmert matrix of dimension n.
  1063.  
  1064.  
  1065.           function Index( lower,  upper, step : integer): vmatrixptr;
  1066.  
  1067.  
  1068.  
  1069.  
  1070.  
  1071.           Mat-P 21
  1072.  
  1073.           Returns an index matrix with elements between lower and upper,
  1074.           and in increments of step. If lower is less than upper, then step
  1075.           must be positive. If lower is greater than upper, then step must
  1076.           be negative.
  1077.  
  1078.  
  1079.           function Kron(a,b:vmatrixptr): vmatrixptr;
  1080.  
  1081.           Returns the Kroniker product of a and b. The blocks of the
  1082.           Kroniker product are a^.m(i,j)*b. 
  1083.  
  1084.  
  1085.  
  1086.  
  1087.  
  1088.           Mat-P 22
  1089.  
  1090.           function Det(Datain : vmatrixptr): double;
  1091.  
  1092.           Returns the determinant of Datain, which must be a square matrix.
  1093.  
  1094.  
  1095.           function Cholesky(ROp: vmatrixptr): vmatrixptr;
  1096.  
  1097.           Returns the Cholesky decomposition of a square symmetric matrix:
  1098.           ROp = S'S, where S is an upper triangular matrix. Cholesky stops
  1099.           if ROp is singular. This routine does not use pivoting.
  1100.  
  1101.  
  1102.           procedure QR(var ROp, Q, R: vmatrixptr; makeq: boolean);
  1103.  
  1104.           This procedure produces the QR decomposition of an rxc matrix:
  1105.           ROp=QR, where Q is an rxc matrix of orthogonal columns, and R is
  1106.           an upper triangular cxc matrix. This routine stops if a zero is
  1107.           detected along the diagonal of R. The variable makeq determines
  1108.           if Q is produced. The procedure calculates Q = ROp*Inv(R). If the
  1109.           calculation of Q is not required, or inversion of R is
  1110.           prohibative, then set makeq = FALSE to avoid the inversion of R.
  1111.  
  1112.  
  1113.           function Svd(var A, U, S, V : vmatrixptr;
  1114.                        makeu, makev: boolean) : integer;
  1115.  
  1116.           This function returns an integer indicating that the singular
  1117.           value decomposition has failed. The singular value decomposition
  1118.           is calculated as A = UDiag(S)V' where U and V are orthogonal
  1119.           matrices and S is a column vector containing the singular values
  1120.           of A. The singular values of A are the eigenvalues of A'A. If you
  1121.           wish to calculate U or V, then set makeu = TRUE or makev = TRUE
  1122.           respectively. 
  1123.  
  1124.  
  1125.           function Ginv( ROp : vmatrixptr): vmatrixptr;
  1126.  
  1127.           This function returns the generalized inverse from the singular
  1128.           value decomposition of ROp=UDiag(S)V'. The generalized inverse of
  1129.           ROp is Ginv(ROp) = VDiag(S+)U', where S+ is a diagonal matrix of
  1130.           the reciprocals of S. If S has a element of zero, the
  1131.           corresponding element of S+ is also zero.
  1132.  
  1133.  
  1134.           function Fft(ROp : vmatrixptr; INZEE : boolean): vmatrixptr;
  1135.  
  1136.           This function returns the fast Fourier transform of an input
  1137.           matrix ROp. The length of ROp may be any value. ROp may have 1 or
  1138.           2 columns. If it has 1 column, the vector is assumed to be a real
  1139.           vector. If the matrix has 2 columns, the matrix is assumed to
  1140.           consist of complex numbers. The first column contains the real
  1141.           part, and the second column contains the imaginary part. The
  1142.  
  1143.  
  1144.  
  1145.  
  1146.  
  1147.           Mat-P 23
  1148.  
  1149.           forward transform is calculated if INZEE is TRUE, and the inverse
  1150.           transform is calculated if INZEE is FALSE.
  1151.  
  1152.  
  1153.           function Vec( ROp : vmatrixptr): vmatrixptr;
  1154.  
  1155.           This functions stacks the columns of a matrix into a single
  1156.           column vector. Thus if ROp is rxc, then Vec(ROp) has r*c rows and
  1157.           1 column. The c blocks of Vec(ROp) are columns of ROp.
  1158.  
  1159.  
  1160.           function Diag(ROp : vmatrixptr): vmatrixptr;
  1161.  
  1162.           This function has two purposes. If the input matrix is a column
  1163.           or row vector, then Diag places the elements along the diagonal
  1164.           elements of square matrix of zeros. If the input matrix is
  1165.           square, then all of the off-diagonal elements are set to zero.
  1166.  
  1167.  
  1168.           function Shape(ROp: vmatrixptr; nrows: integer): vmatrixptr;
  1169.  
  1170.           This functions reshapes a matrix to have nrows rows. If ROp is
  1171.           has r*c elements, then nrows must divide r*c without a remainder.
  1172.  
  1173.  
  1174.           type  margins = (ALL,ROWS,COLUMNS);
  1175.  
  1176.           This enumerated type controls the actions of the next 5
  1177.           functions. If you use ALL, then the functions operate over all
  1178.           elements of the input matrix. If you use ROWS, then the functions
  1179.           operate over the rows of the input. If you use COLUMNS, then the
  1180.           functions operate over the columns of the matrix.
  1181.  
  1182.  
  1183.           function Sum(   ROp : vmatrixptr; marg : margins ) : vmatrixptr;
  1184.  
  1185.           This functions returns sum of the elements in a matrix. If
  1186.           marg=ALL, then the returned matrix is 1x1, containing the sum of
  1187.           all elements. If marg=ROWS then the return matrix has the same
  1188.           number of columns as ROp, and 1 row. Each column contains the sum
  1189.           of the row elements. If marg=COLUMNS then the return matrix has
  1190.           the same number of rows as ROp, and 1 column. Each row cantains
  1191.           the sum of the column elements.
  1192.  
  1193.  
  1194.           function Sumsq( ROp : vmatrixptr; marg: margins): vmatrixptr;
  1195.  
  1196.           This function is similar to Sum, except that it returns the sum
  1197.           of squared elements.
  1198.  
  1199.  
  1200.  
  1201.  
  1202.  
  1203.           Mat-P 24
  1204.  
  1205.           function Cusum( ROp : vmatrixptr): vmatrixptr;
  1206.  
  1207.           This function returns the cumulative sum across rows of matrix.
  1208.  
  1209.  
  1210.           function Mmax( ROp: vmatrixptr; marg : margins): vmatrixptr;
  1211.           function Mmin( ROp: vmatrixptr; marg : margins): vmatrixptr;
  1212.  
  1213.           These functions find the max and min elements of a matrix, and
  1214.           the position of the elements. If marg=ALL, then the functions
  1215.           return a 3x1 matrix. The first element is the row index of the
  1216.           max, the second element is the column index of the element, and
  1217.           the third element is the max(or min). If marg=ROWS, then the
  1218.           returned matrix is a 3xc matrix where the first row is the row
  1219.           index of the max(min) element in a column, the second row is the
  1220.           column index of the max(min) element, and the third row are the
  1221.           max(min) for the column. Similarly, if marg=COLUMNS, then a rx3
  1222.           matrix is returned.
  1223.  
  1224.  
  1225.           Procedure Crow(var ROp: vmatrixptr; row : integer; c : double);
  1226.           Procedure Srow(var ROp : vmatrixptr; row1, row2 : integer);
  1227.           Procedure Lrow(var ROp : vmatrixptr; row1, row2 : integer; c :    
  1228.             double);
  1229.           Procedure Ccol(var ROp : vmatrixptr; col : integer; c : double);
  1230.           Procedure Scol(var ROp : vmatrixptr; col1, col2 : integer);
  1231.           Procedure Lcol(var ROp : vmatrixptr; col1, col2 : integer; c :    
  1232.             double);
  1233.  
  1234.  
  1235.           These functions perform elementary row and column operations on
  1236.           matrices. They change the input matrices. The first letter
  1237.           indicates the operation. The next 3 letters indicate if the
  1238.           operation is for rows or columns. For Crow, the row indexed by
  1239.           row is multiplied by c. For Srow, row1 and row2 are swapped. For
  1240.           Lrow, row1 is multiplied by c, and added to row2. Ccol, Scol, and
  1241.           Lcol perform similar operations on columns.
  1242.  
  1243.  
  1244.  
  1245.           V. COMPILATION AND LIMITATIONS
  1246.  
  1247.  
  1248.           Programs using PMAT must include pmat in the uses statement. Also
  1249.           put pmatop in the uses statement if you use code from PMATOP.PAS.
  1250.  
  1251.           Compile and link the program. See mattest2.pas or testop.pas for 
  1252.           example files using PMat or PMatop. Also see makefile for a
  1253.           Borland make file for using the command line compiler. 
  1254.  
  1255.  
  1256.  
  1257.  
  1258.  
  1259.           Mat-P 25
  1260.  
  1261.           PMat does have some limitations. It is limited by the available
  1262.           memory in the machine, so large matrices will exhaust the heap,
  1263.           and the program will stop. This is bothersome on a PC without a
  1264.           DOS extender. The BP7 protected mode for DOS programs allows up
  1265.           to 16 megs of memory, and matrices may be larger than 640K. Note
  1266.           that a 1000x1000 of double takes 8 megs, so PMat may not be
  1267.           suitable for really large matrices.
  1268.  
  1269.           Another limitation is its readability. Using function calls
  1270.           rather than overloaded algebraic operators muddles things.  There
  1271.           is no other obvious way to do this in TP though. One trick I use
  1272.           to help me with this is to count open-close parenthesis pairs.
  1273.           Some programming editors help this too by blink the other member
  1274.           of the pair. Another trick is to keep recursive expressions on a
  1275.           single line. This helps with storage also, since the stack
  1276.           storage is cleaned up after each call to equals. If you have
  1277.           memory overflow problems, try splitting the recursive expressions
  1278.           into several parts by paying attention to where the big matrices
  1279.           are created. 
  1280.  
  1281.  
  1282.  
  1283.           VI. REVISION HISTORY
  1284.  
  1285.  
  1286.  
  1287.                A.  Version 1.0
  1288.  
  1289.           This is the original version. It is a copy of my matrix program
  1290.           C-Mat. I may extend PMAT to have most of the capability of YAMP,
  1291.           my C++ program. This would involve a virtual memory manager, a
  1292.           graphics module, and larger set of mathematical functions. I
  1293.           don't know if it is worth the effort though.
  1294.  
  1295.                B.  Version 1.1
  1296.  
  1297.           This version adds BP 7.0 compatability for protected mode DOS
  1298.           applications. The version 7 DPMI extender eliminates the need of
  1299.           a virtual memory manager. You need a 286 or 386 to use this
  1300.           feature.
  1301.  
  1302.                C.  Version 1.2
  1303.  
  1304.           Added a new module for the math functions from YAMP. This
  1305.           includes matrix sorts, elementwise functions, trace, helmert
  1306.           matrix, index matrix, Kroniker products, determinants, Cholesky
  1307.           decomposition, QR decomposition, singular valued decomposition,
  1308.           generalized inverse, fast Fourier transform, vec, diagonal,
  1309.           shape, sums, sums of squares, cumulative sums, max, min, and
  1310.           elementary row-column operations. Added arithmetic operations
  1311.           with scalars: scadd, addsc, etc.